package com.dtx.factory;

import com.dtx.day5.day05Annotation.Component;
import com.dtx.day5.day05Annotation.Resource;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.net.URL;
import java.util.*;


public class AnnotationBeanFactory {

    private static Map<String,Object> beans = new HashMap<>();

    static {
        try {
            // 扫描 com.dtx 包下的所有 java代码，查找类中，是否添加了 @Component注解
            URL resource = AnnotationBeanFactory.class.getResource("/com/dtx");
            //System.out.println(resource.getPath());
            String path = "file://" + resource.getPath();
            File file = new File((new URI(path)));
            // com.dtx 目录下的所有 .class文件
            List<String> strings = searchClassFile(file);

            // 对集合中存放的所有类，进行类加载
            for (String clazz : strings) {
                Class<?> aClass = Class.forName(clazz);

                // 判断类上是否有 @Component 注解
                if (aClass.isAnnotationPresent(Component.class)) {

                    // 创建 该类的对象
                    Object obj = aClass.getDeclaredConstructor().newInstance();
                    // 获取 注解
                    Component com = aClass.getAnnotation(Component.class);

                    String key = com.value();

                    if ("".equals(key)) {

                        // 获取当前类的名字
                        key = clazz.substring(clazz.lastIndexOf(".") + 1);
                    }
                    if (beans.containsKey(key)) throw new Exception("bean的ID【"+key+"】重复，请检查代码");
                    beans.put(key,obj);
                }
            }

            // 组装 bean
            beans.forEach((k,v) -> {
                try {
                    weavingResource(v);

                    // 对 目标对象 进行代理，添加日志
                    Object proxy = ProxyLoggerFactoryBean.getProxy(v);
                    beans.put(k,proxy);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static List<String> searchClassFile(File file) throws Exception{
        List<String> list = new ArrayList<>();
        File[] files = file.listFiles(f -> f.isDirectory() || f.getName().endsWith(".class"));

        for (File f : files) {

            if (f.isFile()) {
                String path = f.getCanonicalPath();
                // 截取 path 字符串
                path = path.substring(path.indexOf("com"+File.separator+"dtx"));
                // 将路径中的 / 替换成 .
                path = path.replace(".class", "").replace(File.separator,".");
                list.add(path);
            }else {
                list.addAll(searchClassFile(f));
            }
        }
        return list;
    }

    public static Object getBean(String beanId) throws Exception{
        return beans.get(beanId);
    }

    public static <T> T getBean(String beanId,Class<T> clz) throws Exception{
        Object bean = getBean(beanId);
        if (bean.getClass() == clz) {
            return (T) bean;
        }
        long count = Arrays.stream(bean.getClass().getInterfaces()).filter(c -> c == clz).count();
        if (count > 0) {
            return (T) bean;
        }
        throw new ClassCastException("类型转换异常");
    }

    public static <T> T getBean(Class<T> clz){
        T obj = null;
        int num = 0; // 记录找到的 bean的个数
        StringJoiner sb = new StringJoiner(",");
        for (Map.Entry<String, Object> entry : beans.entrySet()) {

            // 获取 value
            Object value = entry.getValue();
            long count = Arrays.stream(value.getClass().getInterfaces()).filter(c -> c == clz).count();
            if (value.getClass() == clz) {
                obj = (T) value;
                num ++;
                sb.add(entry.getKey());
            }else if(count > 0){
                obj = (T) value;
                num ++;
                sb.add(entry.getKey());
            }

        }
        if (num > 1) throw new RuntimeException("期待返回1个对象，实际容器中有"+num+"个对应的对象,分别是"+sb);
        return obj;
    }


    private static void weavingResource(Object obj) throws Exception{

        // 获取对象中的所有属性
        Class<?> clz = obj.getClass();
        // 获取所有的属性
        Field[] fields = clz.getDeclaredFields();

        for (Field field : fields) {

            if (field.isAnnotationPresent(Resource.class)) {
                Resource res = field.getAnnotation(Resource.class);

                // 获取 beanId
                String beanId = res.value();

                Object bean = null;
                if (!"".equals(beanId)) {
                    bean = getBean(beanId);
                }else {
                    // 先根据属性名找
                    bean = getBean(field.getName());
                    if(bean == null){
                        // 根据 对应属性的类型
                        Class<?> type = field.getType();
                        bean = getBean(type);
                    }

                }
                if(bean == null) throw new Exception("beanId为【"+beanId+"】的对象找不到");

                // 将 bean注入到属性中
                field.setAccessible(true);
                field.set(obj,bean);
            }
        }

        // 获取所有的方法
        Method[] methods = clz.getMethods();

        // 遍历
        for (Method method : methods) {

            // 判断方法名是否以 set开头
            if (method.getName().startsWith("set") && method.isAnnotationPresent(Resource.class)) {
                // 判断方法上是否有 Resource 注解
                Resource res = method.getAnnotation(Resource.class);

                // 获取 beanId
                String beanId = res.value();

                Object bean = null;
                if (!"".equals(beanId)) {
                    bean = getBean(beanId);
                }else {
                    // 获取方法名
                    String methodName = method.getName().replaceFirst("set", "");
                    methodName = String.valueOf(methodName.charAt(0)).toLowerCase() + methodName.substring(1);
                    bean = getBean(methodName);
                    if(bean == null){
                        // 根据 对应属性的类型
                        Class<?> type = method.getParameterTypes()[0];
                        bean = getBean(type);
                    }

                }
                if(bean == null) throw new Exception("beanId为【"+beanId+"】的对象找不到");

                // 找到的话
                method.invoke(obj,bean);

            }
        }
    }


    public static void main(String[] args) {

    }
}
